//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

/*
 * Use for ftl, Test BufferSize is 2, 4, 8, 16, 32, 128, 512, 1024, 2048, 4096, etc.
 */

/* Intel Flash Type : L18 256Mbit, Bottom */
#define INTEL_CACHE_SIZE      32
#define INTEL_BYTES_BLOCK     65536    // Number of words per block

EXTERN_C int NorFlashEraseSector(unsigned long ulFlashBase, long lSectorOffset)
{
    int blocks;
    unsigned long blkadr;

    blocks  = 0;
    blkadr  = ulFlashBase + lSectorOffset;

    /* unlocking */
    volatile UInt16 *tmpadr= (volatile UInt16*)blkadr;
    UInt16 status;

    /* clear status */
    *tmpadr = 0x0050;
    /* send clear command */
    *tmpadr = 0x0060;
    *tmpadr = 0x00d0;

    do {
        status = *tmpadr;
    }while( (status & 0x0080) != 0x0080);

    if( status != 0x0080) {
        return 1;
    }

    *tmpadr = 0x00ff;

    /* erasing */
    tmpadr = (volatile UInt16*)blkadr;

    /* clear status */
    *tmpadr = 0x0050;
    /* send erase command */
    *tmpadr = 0x0020;
    *tmpadr = 0x00d0;

    /* BUSY? */
    do {
        status = *tmpadr;
    }while( (status & 0x0080) != 0x0080);

    /* erase error? */
    if( (status & 0x0020) == 0x0020) {
        return 1;
    }

    *tmpadr = 0x00ff;

    return 0;
}

//****************************************************************************
//
// Read a block of data into DRAM from Flash memory
//
//****************************************************************************
EXTERN_C int NorFlashReadBuffer  (unsigned long ulFlashBase, long par, unsigned char *Buffer, long BufferSize)
{
//    volatile unsigned short* blkadr = (volatile unsigned short*)((ulFlashBase + par) & ~(INTEL_BYTES_BLOCK-1));//block adr
//    *blkadr = 0x00ff;//read ready

    long cb = BufferSize;
    if (((Address)Buffer | (Address)(ulFlashBase+par) | cb) & 3) {
        unsigned short* p = (unsigned short*) (Buffer);    // not natural aligned 4
        unsigned short * q = (unsigned short*) (ulFlashBase+par);
        int count = cb >> 1;
        while (count--) {
            *p++ = *q++;            // copy two byte at a time
        }
    }
    else {                          // in case of natural alignment
        unsigned long* p = (unsigned long*) (Buffer);
        unsigned long* q = (unsigned long*) (ulFlashBase+par);
        int count = cb >> 2;
        while (count--) {
            *p++ = *q++;            // copy 4 bytes at a time
        }
    }
    return 0;
}

EXTERN_C int FlashReady(unsigned long ulFlashBase, long lSectorOffset)
{
    unsigned long blkadr;

    blkadr  = ulFlashBase + lSectorOffset;

    /* unlocking */
    volatile UInt16 *tmpadr= (volatile UInt16*)blkadr;
    UInt16 status;

    /* clear status */
    *tmpadr = 0x0050;
    /* send clear command */
    *tmpadr = 0x0060;
    *tmpadr = 0x00d0;

    do {
        status = *tmpadr;
    }while( (status & 0x0080) != 0x0080);

    if( status != 0x0080) {
        return 1;
    }

    *tmpadr = 0x00ff;

    return 0;
}

static int do_write_buffer_16bit(UInt32 dest, unsigned char* mem, UInt32 len);
//****************************************************************************
//
// Writes a block of data into the FLASH memory.
//
//****************************************************************************
//FlashWriteBuffer func in the driver had been tested with some sample data.
//such as:write 2, 4, 512, 1024 bytes into the flash. All the results are correct.
EXTERN_C int NorFlashWriteBuffer(unsigned long ulFlashBase, long par, unsigned char *Buffer, long BufferSize)
{
    int  ret;
    unsigned long blkadr = ulFlashBase + par;
    unsigned short* source = (unsigned short*)Buffer;
    UInt32 left, writelen;

    /* unlocking */
    volatile UInt16 *tmpadr= (volatile UInt16*)blkadr;
    UInt16 status;

    /* clear status */
    *tmpadr = 0x0050;
    /* send clear command */
    *tmpadr = 0x0060;
    *tmpadr = 0x00d0;

    do {
        status = *tmpadr;
    }while( (status & 0x0080) != 0x0080);

    if( status != 0x0080) {
        return 1;
    }

    /* start write data into flash */
    left = (UInt32)BufferSize;
    while ( blkadr < (ulFlashBase + par + BufferSize) ) {

        writelen = (left > INTEL_BYTES_BLOCK) ? INTEL_BYTES_BLOCK : left;

        ret = do_write_buffer_16bit(blkadr, (unsigned char*)source, writelen);
        if (ret) return ret;

        source += writelen>>1;
        blkadr += INTEL_BYTES_BLOCK;
        left -= writelen;
    }

    return 0;
}

int do_write_buffer_16bit(UInt32 dest, unsigned char* mem, UInt32 len)
{
    UInt32 cache_size, count, i;
    unsigned short status;
    unsigned short *data;
    volatile unsigned short *blkadr, *start;

    /* Buffer write */
    cache_size = INTEL_CACHE_SIZE;

    blkadr = (volatile unsigned short*) dest;

    start = blkadr;
    data = (unsigned short*) mem;

    while( len > 0 ) {

        if( len > cache_size) count = cache_size >>1;
        else count = len >>1;
        /* is buffer avaible? */
        do {
            /* issue write buffer command to block address */
            *blkadr = 0x00e8;

            /* read extended status register at block address */
            status = *blkadr;

        }while( (status & 0x0080) != 0x0080);

        /* write word count - block address */
        *blkadr = (count-1);

        /* write buffer data - start address */
        for(i=0; i<count; i++) {
            *start++ = *data++;
        }

        /* write confirm 0xD0 to block address */
        *blkadr = 0x00d0;

        do{
            status = *blkadr;
        }while( (status & 0x0080) != 0x0080);

        /* program error? */
        if((status & 0x0010) == 0x0010) {
            return 1;
        }
        len  -= (count << 1);
    }

    /* read-array mode */
    *blkadr = 0x00ff;

    return 0;
}

